/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#include "clock.h"
#include "atc.h"

#include <qtimer.h>


using namespace atc;
using std::make_pair;
using std::string;

////////////////////////////////////////////////////////////////////////////////
//
// atc::Clock
//
//------------------------------------------------------------------------------
// [public] construction
//

/*!
 *
 */
Clock::Clock()
	  : _time    ()
	  , _timer   ( new QTimer( this ) )
	  , _alarms  ()
	  , _running ( false )
	  , _tickAt  ( 0 )
	  , _elapsed ( 0 )
	  , _milliseconds ( 0 )
	  , _xspeed( 1.0 )
{
	connect( _timer, SIGNAL( timeout() ), this, SLOT( tick_dispatch() ) );
}

/*!
 *
 */
Clock::~Clock() {}


//------------------------------------------------------------------------------
// [public]
//

/*!
 *
 */
void Clock::set_alarm( int timeout, QObject *receiver, const char *slot ) {
	QTimer *tmr = new QTimer( this );
	connect( tmr, SIGNAL( timeout() ), receiver, slot );
	if ( _running )
			tmr->start( timeout / _xspeed, true );
	Alarm a( tmr, timeout, elapsed_time() );
	_alarms.push_back( a );

	KeyValues kvs;
	kvs.push_back( make_pair( string("clock"), string("set_alarm") ) );
	kvs.push_back( make_pair( string("timeout"), to_string(timeout) ) );
	kvs.push_back( make_pair( string("receiver"), to_string(receiver) ) );
	kvs.push_back( make_pair( string("slot"), to_string(slot) ) );
	emit event_recorded( kvs );
}

/*!
 *
 */

long Clock::elapsed_time() {
	if ( _running ) _elapsed += _time.restart();
	return _elapsed;
}


//------------------------------------------------------------------------------
// [public] slots
//

/*!
 *
 */
void Clock::start() {
	if ( _running ) return;

	KeyValues kvs;
	kvs.push_back( make_pair( string( "clock"    ), to_string( "start_request" ) ) );
	kvs.push_back( make_pair( string( "interval" ), to_string( _tickAt       ) ) );
	emit event_recorded( kvs );

	_running = true;
	for ( AlarmIt ait = _alarms.begin(); ait != _alarms.end(); ++ait ) {
		(*ait).elapsed = _elapsed;
		(*ait).timer->start( (*ait).timeout / _xspeed, true );
	}
	_time.start();
	_timer->start( _tickAt / _xspeed );
	emit started();
	emit seconds( _milliseconds / ONE_SECOND );

}

/*!
 *
 */
void Clock::resume() { start(); }

/*!
 *
 */
void Clock::pause() { stop(); }

/*!
 * Stops the clock.
 *
 * Actually only sets the flag to stop the clock - the clock doesn't actually stop until
 * the next tick. @sa tick_dispatch()
 */
void Clock::stop() {
	if ( !_running ) return;

	_running = false;

	KeyValues kvs;
	kvs.push_back( make_pair( string( "clock"    ), to_string( "stop_request" ) ) );
	emit event_recorded( kvs );
}


//------------------------------------------------------------------------------
// [private] slots
//

/*!
 *
 */
void Clock::tick_dispatch() {
	_milliseconds += _tickAt;
	
	KeyValues kvs;
	kvs.push_back( make_pair( string( "clock" ), to_string( "tick" ) ) );
	kvs.push_back( make_pair( string( "seconds" ), to_string( _milliseconds / ONE_SECOND ) ) );
	emit event_recorded( kvs );

	emit seconds( _milliseconds / ONE_SECOND );
	emit tick( _tickAt );

	if ( !_running ) {
		_elapsed += _time.elapsed();

		_timer->stop();
		for ( AlarmIt ait = _alarms.begin(); ait != _alarms.end(); ++ait ) {
			(*ait).timer->stop();
			(*ait).timeout -= (_elapsed - (*ait).elapsed) * _xspeed;
		}
		
		emit stopped();

		KeyValues kvs;
		kvs.push_back( make_pair( string( "clock" ), to_string( "stop" ) ) );
		emit event_recorded( kvs );
	}
}

////////////////////////////////////////////////////////////////////////////////
